#include <linux/sched.h>
#include <linux/kernel.h>
#include <asm/segment.h>
#include <signal.h>
#include <sys/wait.h>
#include <errno.h>

int send_sig (int, struct task_struct *, int);

int sys_sgetmask()
{
	return current->blocked;
}

int sys_ssetmask(int newmask)
{
	int old = current->blocked;
	current->blocked = newmask & ~(1<<(SIGKILL-1)) & ~(1<<(SIGSTOP-1));
	return old;
}

int sys_sigpending(sigset_t *set)
{
	verify_area(set, 4);
	put_fs_long(current->blocked & current->signal, (unsigned long*)set);
	return 0;
}

int sys_sigsuspend(int restart, unsigned long old_mask, unsigned long set)
{
	extern int sys_pause(void);

	if (restart) {
		current->blocked = old_mask;
		return -EINTR;
	}

	__asm__("movl $1, %0\n\t"::"m"(restart));
	__asm__("movl %%eax, %0\n\t" : : "m"(old_mask), "a"(current->blocked));
	current->blocked = set;
	(void) sys_pause();
	return -ERESTARTNOINTR;
}

static inline void save_old(char* from, char* to)
{
	int i;

	verify_area(to, sizeof(struct sigaction));
	for (i=0; i < sizeof(struct sigaction); i++) {
		put_fs_byte(*from, to);
		from++;
	}
}

static inline void get_new(char* from, char* to)
{
	int i;
	for (i=0; i<sizeof(struct sigaction); i++) {
		*(to++) = get_fs_byte(from++);
	}
}

int sys_signal(int signum, long handler, long restorer)
{
	struct sigaction tmp;

	if (signum < 1 || signum > 32 || signum == SIGKILL || signum == SIGSTOP)
		return -EINVAL;
	tmp.sa_handler = (void (*)(int)) handler;
	tmp.sa_mask = 0;
	tmp.sa_flags = SA_ONESHOT | SA_NOMASK;
	tmp.sa_restorer = (void (*)(void)) restorer;
	handler = (long)current->sigaction[signum-1].sa_handler;
	current->sigaction[signum - 1] = tmp;
	return handler;
}

int sys_sigaction(int signum, const struct sigaction* action, struct sigaction* oldaction)
{
	struct sigaction tmp;

	if (signum< 1|| signum>32 || signum == SIGKILL || signum == SIGSTOP)
		return -EINVAL;
	tmp = current->sigaction[signum-1];
	get_new((char*)action, (char*)(signum-1 + current->sigaction));
	if (oldaction)
		save_old((char*)&tmp, (char*)oldaction);
	if (current->sigaction[signum-1].sa_flags & SA_NOMASK)
		current->sigaction[signum-1].sa_mask = 0;
	else 
		current->sigaction[signum-1].sa_mask |= (1<<(signum-1));
	return 0;
}

int core_dump(long signr)
{
	return 0;
}

extern int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options);

int do_signal(long signr,long ebx, long ecx, long edx,
	      long esi, long edi, long ebp, long eax,
	      long ds, long es, long fs, long gs,
	      long orig_eax,
	long eip, long cs, long eflags,
	unsigned long * esp, long ss)
{
	unsigned long sa_handler;
	long old_eip = eip;
	struct sigaction *sa = current->sigaction + signr-1;
	int longs;

	unsigned long* tmp_esp;
#ifdef notdef
	printk("pid: %d, signr: %d, eax=%d, oeax=%d, int=%d\n",
		current->pid, signr, eax, orig_eax, sa->sa_flags & SA_INTERRUPT);
#endif 
	if ( (orig_eax != -1) && 
		(eax == -ERESTARTSYS || eax == -ERESTARTNOINTR) )
	{
		if ( eax == -ERESTARTSYS && 
			((sa->sa_flags & SA_INTERRUPT) || signr < SIGCONT || signr > SIGTTOU) )
		{
			*(&eax) = -EINTR;
		} else {
			*(&eax) = orig_eax;
			old_eip -= 2;
			__asm__("movl %%eax, %0\n\t" : : "m"(eip), "a"(old_eip));
		}
	}

	sa_handler = (unsigned long)sa->sa_handler;
	if (sa_handler==1) {
/* check for SIGCHLD: it's special */
		if (signr == SIGCHLD)
			while (sys_waitpid(-1,NULL,WNOHANG) > 0)
				/* nothing */;
		return(1);   /* Ignore, see if there are more signals... */
	}
	if (!sa_handler) {
		switch (signr)
		{
		case SIGCONT:
		case SIGCHLD:
        case SIGWINCH:
			return 1;
		case SIGSTOP:
		case SIGTSTP:
		case SIGTTIN:
		case SIGTTOU:
			current->state = TASK_STOPPED;
			current->exit_code = signr;
			if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags & SA_NOCLDSTOP))
				send_sig(SIGCHLD, current->p_pptr, 1);
			return 1;
		case SIGQUIT:
		case SIGILL:
		case SIGTRAP:
		case SIGIOT:
		case SIGFPE:
		case SIGSEGV:
			if (core_dump(signr))
				do_exit(signr + 0x80);
		default:
			do_exit(signr);
		}
	}

	if (sa->sa_flags & SA_ONESHOT)
		sa->sa_handler = NULL;
	*(&eip) = sa_handler;
	longs = (sa->sa_flags & SA_NOMASK) ? 7 : 8;
	__asm__("subl %1, %0\n\t" : :"m"(esp), "a"(longs*4));
	verify_area(esp, longs*4);
	tmp_esp = esp;
	put_fs_long((long)sa->sa_restorer, tmp_esp++);
	put_fs_long(signr, tmp_esp++);
	if (!(sa->sa_flags & SA_NOMASK))
		put_fs_long(current->blocked, tmp_esp++);
	put_fs_long(eax, tmp_esp++);
	put_fs_long(ecx, tmp_esp++);
	put_fs_long(edx, tmp_esp++);
	put_fs_long(eflags, tmp_esp++);
	put_fs_long(old_eip, tmp_esp++);
	current->blocked |= sa->sa_mask;
    /* force a supervisor-mode page-in of the signal handler to reduce races */
	__asm__("testb $0,%%fs:%0"::"m" (*(char *) sa_handler));
	return 0;
}